#+---------------------------------------------+
#| ProxyBot 0.7
#+---------------------------------------------+
#| Written by Alec Hussey
#| Copyright (C) 2008 MadSoft
#| Website: www.madsoft.org
#| License: GNU General Public License v3
#+---------------------------------------------+

from config import *
from socket import *
from scanner import *
from database import *
import sys, time, botlib

class ProxyBot(botlib.Bot):
	def __init__(self, server, channel, nick, nickserv, config_file):
		botlib.Bot.__init__(self, server, 6667, channel, nick)
		self.invalid_chars = "abcdefghijklmnopqrstuvwrxyzABCDEFGHIJKLMNOPQRSTUVWRXYZ`~!@#$%^&*()_-+={[}]\|;:\"'<,>.?/"
		
		# Open configuration file
		try:
			self.config = Config(config_file)
		except RuntimeError, msg:
			print "Error: %s" % msg[1]
			sys.exit(1)
		
		# Make connection to the database
		try:
			self.db = Database(self.config.get("dbtype"), self.config.get("dbhost"), self.config.get("dbname"), self.config.get("dbuser"), self.config.get("dbpass"))
		except RuntimeError:
			print "Error: Failed to connect to database"
			sys.exit(1)
		
		# Send nickserv password if availible
		if nickserv != None:
			self.protocol.privmsg("nickserv", "identify %s" % nickserv)
	
	def __actions__(self):
		botlib.Bot.__actions__(self)
		
		# Determine whether to send a NOTICE or PRIVMSG
		self.notice = botlib.check_found(self.data, "PRIVMSG %s" % self.nick)
		
		# Check and respond to individual commands
		if botlib.check_found(self.data, "!portscan"):
			self.args = self.get_args()
			self.username = self.get_username()
			self.hostname = self.get_hostname()
			
			if len(self.args) == 2:
				# Check for successful start
				try:
					# Spawn new thread for the scan
					Scanner(self).start()
				except RuntimeError, msg:
					self.send_message(msg)
			else:
				self.send_message("Invalid syntax. Try !commands portscan")
		elif botlib.check_found(self.data, "!portsearch"):
			self.args = self.get_args()
			
			if len(self.args) == 2:
				if self.args[0] == "ip":
					# Query all ports which match the given IP address
					self.db.cursor.execute("SELECT port, time FROM ports WHERE ip=%s;", (self.args[1],))
					results = self.db.cursor.fetchall()
					
					# If results were returned, loop through displaying them to the user
					if len(results) > 0:
						count = 0
						self.send_message("Results (IP %s):" % self.args[1])
						for row in results:
							# Increment counter and handle flood protection
							count += 1
							if count == 4:
								count = 0; time.sleep(1)
							self.send_message("Port: %s Time: %s" % (row[0], row[1]))
					else:
						self.send_message("No results found.")
				elif self.args[0] == "port":
					# Query all IP addresses which match the given port
					self.db.cursor.execute("SELECT ip, time FROM ports WHERE port=%s;", (self.args[1],))
					results = self.db.cursor.fetchall()
					
					# If results were returned, loop through displaying them to the user
					if len(results) > 0:
						count = 0
						self.send_message("Results (Port %s):" % self.args[1])
						for row in results:
							# Increment counter and handle flood protection
							count += 1
							if count == 4:
								count = 0; time.sleep(1)
							self.send_message("IP: %s Time: %s" % (row[0], row[1]))
					else:
						self.send_message("No results found.")
				else:
					"""
					Since the first argument was either not given or did not
					match any of the required keywords, then display a message
					which notifies the user that they have made a mistake
					"""
					self.send_message("Invalid syntax. Try !commands portsearch")
			else:
				"""
				Since the number of arguments did not match the required number
				of arguments, notify the user that they have made a mistake
				"""
				self.send_message("Invalid syntax. Try !commands portsearch")
		elif botlib.check_found(self.data, "!showscans"):
			self.args = self.get_args()
			
			if len(self.args) == 1:
				# Query data from database
				self.db.cursor.execute("SELECT username, iprange, portrange, time FROM scans ORDER BY id DESC LIMIT %s", (int(self.args[0]),))
				results = self.db.cursor.fetchall()
				
				# Loop through results returned from database
				if len(results) > 0:
					count = 0
					self.send_message("Results (Last %s):" % self.args[0])
					for row in results:
						# Increment counter and handle flood protection
						count += 1
						if count == 4:
							count = 0; time.sleep(1)
						self.send_message("Nick: %s IP(s): %s Port(s): %s Time: %s" % (row[0], row[1], row[2], row[3]))
				else:
					self.send_message("No results found.")
			else:
				self.send_message("Invalid syntax. Try !commands showscans")
		elif botlib.check_found(self.data, "!gethostbyname"):
			self.args = self.get_args()
			self.username = self.get_username()
			
			if len(self.args) == 1:
				# Attempt to resolve given self.hostname
				try:
					result = gethostbyname(self.args[0])
				except:
					result = "Hostname could not be resolved."
				
				self.send_message("IP: %s" % result)
			else:
				self.send_message("Invalid syntax. Try !commands gethostbyname")
		elif botlib.check_found(self.data, "!commands"):
			self.args = self.get_args()
			self.username = self.get_username()
			commands = (
				"commands;!commands <command>;Gives information and lists the commands availible.",
				"portscan;!portscan <ipaddress[,range]> <port[,range]>;Scans specified ports on a given ip address.",
				"portsearch;!portsearch <ip|port> <port>;Searches the database for all IPs wich match the given port.",
				"showscans;!showscans <num>;Lists the last X number of scans, who did them, and what they scanned.",
				"gethostbyname;!gethostbyname <hostname>;Resolves a given self.hostname to its associated IP address."
			)
			
			if len(self.args) == 0:
				# List all availible commands
				for num in range(len(commands)):
					data = commands[num].split(";")
					self.send_message(data[1])
			else:
				# Search for the requested command
				for num in range(len(commands)):
					data = commands[num].split(";")
					if data[0] == self.args[0]:
						self.send_message(data[1])
						self.send_message("\t\t\t\t%s" % data[2])
						return
				
				self.send_message("Command not found.")
		elif botlib.check_found(self.data, "!whoami"):
			version = self.config.get("version")
			self.send_message(version)
		elif botlib.check_found(self.data, "!kill"):
			version = self.config.get("version")
			self.protocol.disconnect(version)
			sys.exit(0)
	
	def send_message(self, message):
		if not self.notice:
			self.protocol.privmsg(self.channel, message)
		else:
			self.protocol.notice(self.username, message)
